package com.xxdemo.quickstart.webapi;

import com.xxdemo.quickstart.*;
import com.xxdemo.quickstart.database.table.Column;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import static com.xxdemo.quickstart.webapi.CustomerController.ResponseTypeEnum.ERR_CUSTOMER_NOT_REGISTERED;
import static com.xxdemo.quickstart.webapi.CustomerController.ResponseTypeEnum.ERR_UNDEFINED;

@RestController
@RequestMapping("customers")
public class CustomerController {
    private final CustomerRecordMaintainingService _service;
    private final CustomerDtoCopier _dtoCopier;

    public CustomerController(
            CustomerRecordMaintainingService customerQueryService,
            CustomerDtoCopier dtoCopier) {
        this._service = customerQueryService;
        this._dtoCopier = dtoCopier;
    }

    @GetMapping("/index/{id}")
    public Result<CustomerDto> searchById(@PathVariable(value = "id", required = true) Long id){
        Optional<Customer> customer = _service.findCustomerById(id);
        if (!customer.isPresent()){
            return buildErrorMessage(ERR_CUSTOMER_NOT_REGISTERED);
        }
        CustomerDto customerDto =
                _dtoCopier.copyFrom(customer.get());
        return new CustomerQueryResult(customerDto);
    }

    public static NullDataResult<CustomerDto> buildErrorMessage(ResponseTypeEnum typeEnum) {
        return new NullDataResult<>(typeEnum);
    }

    /// 空白Result占位符, 含status状态码和错误信息字符串
    private static class NullDataResult<DataT> implements Result<DataT> {
        @Override
        public DataT getData() {
            return null;
        }

        private final Integer status;
        private final String error;

        public NullDataResult(ResponseTypeEnum msgTypeEnum) {
            status = msgTypeEnum.getStatusCode();
            error = msgTypeEnum.getMsg();
        }

        public NullDataResult() {
            this(ERR_UNDEFINED);
        }

        public Integer getStatus() {
            return status;
        }

        public String getError() {
            return error;
        }
    }

    /// 此处定义本Controller接口的所有API响应类型: 包含code和msg, msg代表错误原因提示字符串, code代表错误码
    public enum ResponseTypeEnum {
        /**
         * 默认返回值code=200表示成功, code=0表示未知或未定义错误, 其他code均表示特定错误已经发生
         */
        SUCCESS("OK", 200),
        ERR_CUSTOMER_NOT_REGISTERED("客户档案未登记", 4040),
        ERR_INVALID_SEARCH_KEYWORD("检测到无效的搜索关键词", 4050),
        ERR_UNDEFINED("抱歉!发生了未知错误", 0);

        private final String msg;
        private final int statusCode;

        ResponseTypeEnum(String msg, int statusCode) {
            this.msg = msg;
            this.statusCode = statusCode;
        }

        public String getMsg() {
            return msg;
        }

        public int getStatusCode() {
            return statusCode;
        }
    }

    /// 按姓名查询
    @GetMapping("/searchByName")
    public MultiCustomerQueryResult searchByName(
            @RequestParam(value = "keyword", required = false) String unsafeSearchKeyword,
            @RequestParam(value = "sortby", required = false, defaultValue = "id") String sortByColumnName,
            @RequestParam(value = "order", required = false, defaultValue = "ASC") String order,
            @RequestParam(value = "limit", required = false, defaultValue = "0") Integer limit) {
        GenericQuerySettings settings;
        SortOrderKeyword descOrAsc = (
                "DESC".equalsIgnoreCase(order) ?
                        SortOrderKeyword.DESCEND :
                        SortOrderKeyword.ASCEND
        );
        Column sortByColumn = new Column(sortByColumnName);
        settings = new GenericQuerySettings(sortByColumn, descOrAsc, limit);
        List<Customer> list
                = _service.findCustomerByNameContains(unsafeSearchKeyword, settings);
        List<CustomerDto> copiedList = _dtoCopier.copyFrom(list);
        return new MultiCustomerQueryResult(copiedList);
    }

    @GetMapping("/index")
    public MultiCustomerQueryResult indexById(
            @RequestParam(value = "idFrom", defaultValue = "1") Long idFrom,
            @RequestParam(value = "to", required = false) Long to,
            @RequestParam(value = "limit", defaultValue = "0") Integer limit) {
        if (idFrom <= 0) {
            idFrom = 1L;
        }
        if (null == to) {
            to = Long.MAX_VALUE;
        }
        CustomerIdNumberRange idRange = CustomerIdNumberRange.betweenTwoNumbers(idFrom, to);
        List<Customer> list = _service.findByIdWithinRange(idRange, limit);
        List<CustomerDto> copiedList = _dtoCopier.copyFrom(list);
        return new MultiCustomerQueryResult(copiedList);
    }

    @GetMapping("/listByPage")
    public MultiCustomerQueryResult listByPage(
            @RequestParam(value = "idGreaterThan", defaultValue = "0") Long customerId,
            @RequestParam(value = "limit", defaultValue = "0") Integer limit) {
        if (customerId < 0) {
            customerId = 0L;
        }
        limit = Math.min(limit, CustomerRecordMaintainingService.maxRecordsPerPage());
        List<Customer> list = _service.listCustomerIdGreaterThan(customerId, limit);
        List<CustomerDto> copiedList = _dtoCopier.copyFrom(list);
        return new MultiCustomerQueryResult(copiedList);
    }

    /// 查询结果含单条客户信息
    public static class CustomerQueryResult implements Result<CustomerDto> {
        private final CustomerDto data;

        /// 默认构造函数
        public CustomerQueryResult(CustomerDto data) {
            this.data = data;
        }

        public CustomerDto getData() {
            return data;
        }
    }

    /// 查询结果列表(含多条客户信息)
    public static class MultiCustomerQueryResult implements Result<List<CustomerDto>> {
        private final List<CustomerDto> data;

        public MultiCustomerQueryResult(List<CustomerDto> data) {
            this.data = data;
        }

        @Override
        public List<CustomerDto> getData() {
            if (null == data) {
                @SuppressWarnings("UnnecessaryLocalVariable")
                List<CustomerDto> emptyList = new ArrayList<>();
                return emptyList;
            }
            return data;
        }
    }
}
